﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.WindowsAzure.StorageClient;
using Microsoft.WindowsAzure;
using DBLOG.Azure.VirtualPathProvider.Entities;
using System.Web;

namespace DBLOG.Azure.VirtualPathProvider
{
    public class BlobStorageDataFactory
    {
        /// <summary>
        /// The dummy file name used to maintain a directory in blob storage
        /// </summary>
        /// <remarks>This file should not displayed in the file editor. When retrieving files this filename is automatically excluded.</remarks>
        public const string CLOUD_PROVIDER_DUMMY_FILE_NAME = "cloudproviderdummyfile.txt";

        static BlobStorageDataFactory _instance = new BlobStorageDataFactory();

        private CloudBlobContainer _BlobContainer;

        private CloudBlobClient _BlobClient;

        public static BlobStorageDataFactory Instance
        {
            get
            {
                // TODO: Implement proper singleton pattern where the container name is automatically fetched from the current VPP instance
                return _instance;
            }
        }

        /// <summary>
        /// Hidden constructor which uses the data connections string for windows azure
        /// </summary>
        /// <remarks>It's the DataConnectionString that defines if we're using Development Storage or not</remarks>
        private BlobStorageDataFactory()
        {
            var cloudAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");

            _BlobClient = cloudAccount.CreateCloudBlobClient();
        }

        public List<CloudBlobDirectory> GetDirectories(string path, string containerName)
        {
            InitializeContainer(containerName);

            CloudBlobDirectory currentDirectory = _BlobContainer.GetDirectoryReference(path);

            return currentDirectory.GetSubdirectories();
        }

        public CloudBlobDirectory GetDirectory(string path, string containerName)
        {
            InitializeContainer(containerName);

            CloudBlobDirectory directory = _BlobContainer.GetDirectoryReference(path);

            if (!directory.Exists())
            {
                return null;
            }
            else
            {
                return directory;
            }
        }

        public List<CloudBlob> GetFiles(string path, string containerName)
        {
            InitializeContainer(containerName);

            CloudBlobDirectory currentDirectory = _BlobContainer.GetDirectoryReference(path);

            return currentDirectory.GetFiles();
        }

        public void CreateDirectory(string path, string containerName)
        {
            InitializeContainer(containerName);

            path = VirtualPathUtility.AppendTrailingSlash(path);

            // Get a reference to the virtual directory
            CloudBlobDirectory blobDirectory = this._BlobContainer.GetDirectoryReference(path);

            // Create the virtual file which will maintain the directory, since it's virtual
            CloudBlob blob = blobDirectory.GetBlobReference(CLOUD_PROVIDER_DUMMY_FILE_NAME);

            // Check if the dummy file already exists at the provided location
            if (blob.Exists())
            {
                throw new BlobStorageProviderException("Folder already exists!");
            }

            AddHiddenFileToMaintainVirtualDirectory(blob);
        }

        private void AddHiddenFileToMaintainVirtualDirectory(CloudBlob blob)
        {
            // Adding some explanatory text to the file should the user browse the directory with another tool than EPiServer CMS
            String blobText = "DO NOT DELETE! This is an automatically generated file from the EPiServer CMS Virtual Path Provider. Needed because Azure directories are virtual and must contain at least one file.";

            UTF8Encoding utf8Encoding = new UTF8Encoding();

            Byte[] bytes = utf8Encoding.GetBytes(blobText);

            using (BlobStream blobStream = blob.OpenWrite())
            {
                blobStream.Write(bytes, 0, bytes.Count<Byte>());
            }
        }

        public CloudBlob CreateFile(CloudDirectory directory, string path, string containerName)
        {
            CloudBlob fileBlob = this._BlobContainer.GetBlobReference(path);

            bool fileExists = fileBlob.Exists();

            if (fileExists)
            {
                // TODO: Notify user that the file exists instead of returning NULL
                return null;
            }
            else
            {
                CloudBlob blob = this._BlobContainer.GetBlobReference(path);

                blob.OpenWrite().Dispose();

                return blob;
            }
        }

        public void CopyFile(CloudDirectory directory, CloudBlob blobToCopy, string newPath, string containerName)
        {
            CloudBlob fileBlob = this._BlobContainer.GetBlobReference(newPath);

            bool fileExists = fileBlob.Exists();

            if (fileExists)
            {
                // TODO: Notify user that the file exists instead of returning NULL
            }
            else
            {
                CloudBlob blob = this._BlobContainer.GetBlobReference(newPath);

                blob.OpenWrite().Dispose();

                blob.CopyFromBlob(blobToCopy);
            }
        }

        public void MoveFile(CloudDirectory directory, CloudBlob blobToMove, string newPath, string containerName)
        {
            CloudBlob fileBlob = this._BlobContainer.GetBlobReference(newPath);

            bool fileExists = fileBlob.Exists();

            if (fileExists)
            {
                // TODO: Notify user that the file exists instead of returning NULL
            }
            else
            {
                CloudBlob blob = this._BlobContainer.GetBlobReference(newPath);

                blob.OpenWrite().Dispose();

                blob.CopyFromBlob(blobToMove);

                blobToMove.Delete();
            }
        }

        public CloudBlob GetFile(string path, string containerName)
        {
            InitializeContainer(containerName);

            CloudBlob blob = _BlobContainer.GetBlobReference(path);

            if (!blob.Exists())
            {
                return null;
            }
            else
            {
                return blob;
            }
        }

        public void DeleteFile(string path, string containerName)
        {
            InitializeContainer(containerName);

            CloudBlob blob = _BlobContainer.GetBlobReference(path);

            blob.DeleteIfExists();
        }

        private void InitializeContainer(string containerName)
        {
            // Container names are only allowed in lowercase
            containerName = containerName.ToLowerInvariant();

            if (containerName.Length > 63 || containerName.Length < 3)
            {
                string errorMessage = String.Format("{0} is not valid. Container names must be from 3 through 63 characters long.", containerName);

                throw new BlobStorageProviderException(errorMessage);
            }

            _BlobContainer = _BlobClient.GetContainerReference(containerName);
            
            _BlobContainer.CreateIfNotExist();
        }
    }
}
